broadway: Implement pointer grabs
authorAlexander Larsson <alexl@redhat.com>
Mon, 14 Mar 2011 10:46:43 +0000 (11:46 +0100)
committerAlexander Larsson <alexl@redhat.com>
Mon, 14 Mar 2011 10:52:46 +0000 (11:52 +0100)
gdk/broadway/broadway.c
gdk/broadway/broadway.h
gdk/broadway/broadway.js
gdk/broadway/gdkdevice-broadway.c
gdk/broadway/gdkdisplay-broadway.c
gdk/broadway/gdkeventsource.c
gdk/broadway/gdkprivate-broadway.h

index b393879d2a9da9576f970746394ca823b8cb0555..bc78de1e58feabf91acdc576e35d2690da6b5894 100644 (file)
@@ -661,6 +661,48 @@ broadway_output_query_pointer (BroadwayOutput *output, int id)
   return serial;
 }
 
+guint32
+broadway_output_grab_pointer (BroadwayOutput *output,
+                             int id,
+                             gboolean owner_event,
+                             guint32 time_)
+{
+  char buf[HEADER_LEN + 3 + 1 + 6];
+  guint32 serial;
+  int p;
+
+  serial = output->serial;
+  p = write_header (output, buf, 'g');
+  append_uint16 (id, buf, &p);
+  buf[p++] = owner_event ? '1': '0';
+  append_uint32 (time_, buf, &p);
+
+  assert (p == sizeof (buf));
+
+  broadway_output_write (output, buf, sizeof (buf));
+
+  return serial;
+}
+
+guint32
+broadway_output_ungrab_pointer (BroadwayOutput *output,
+                               guint32 time_)
+{
+  char buf[HEADER_LEN + 6];
+  guint32 serial;
+  int p;
+
+  serial = output->serial;
+  p = write_header (output, buf, 'u');
+  append_uint32 (time_, buf, &p);
+
+  assert (p == sizeof (buf));
+
+  broadway_output_write (output, buf, sizeof (buf));
+
+  return serial;
+}
+
 void
 broadway_output_new_surface(BroadwayOutput *output,  int id, int x, int y, int w, int h)
 {
index b0d3c2a61f72bb20fff828bc85234e581159af23..c750a2497ba3de5d96b9b3be80409f2b685bb742 100644 (file)
@@ -57,3 +57,9 @@ void            broadway_output_copy_rectangles (BroadwayOutput *output,
                                                 int             dy);
 guint32         broadway_output_query_pointer   (BroadwayOutput *output,
                                                 int id);
+guint32         broadway_output_grab_pointer    (BroadwayOutput *output,
+                                                int id,
+                                                gboolean owner_event,
+                                                guint32 time_);
+guint32         broadway_output_ungrab_pointer  (BroadwayOutput *output,
+                                                guint32 time_);
index da4b711460a99c8d5259493a0c09bfefc69d0c60..acdace7b02fab9a45046c20f6ed241f6014b8e1c 100644 (file)
@@ -76,9 +76,15 @@ function createXHR()
   return null;
 }
 
+var grab = new Object();
+grab.window = null;
+grab.owner_events = false;
+grab.time = 0;
+grab.implicit = false;
 var last_serial = 0;
 var last_x = 0;
 var last_y = 0;
+var real_window_with_mouse = 0;
 var window_with_mouse = 0;
 var surfaces = {};
 var outstanding_commands = new Array();
@@ -99,6 +105,14 @@ function initContext(canvas, x, y, id)
   return context;
 }
 
+var GDK_GRAB_SUCCESS = 0;
+var GDK_GRAB_ALREADY_GRABBED = 1;
+var GDK_GRAB_INVALID_TIME = 2;
+
+var GDK_CROSSING_NORMAL = 0;
+var GDK_CROSSING_GRAB = 1;
+var GDK_CROSSING_UNGRAB = 2;
+
 function handleCommands(cmd_obj)
 {
   var cmd = cmd_obj.data;
@@ -265,6 +279,40 @@ function handleCommands(cmd_obj)
        send_input ("q", [pos.root_x, pos.root_y, pos.win_x, pos.win_x, window_with_mouse]);
        break;
 
+      case 'g': // Grab
+        var id = base64_16(cmd, i);
+        i = i + 3;
+       var owner_events = cmd[i++] == '1';
+       var time = base64_32(cmd, i);
+       i = i + 6;
+
+       if (grab.window != null) {
+           /* Previous grab, compare times */
+           if (time != 0 && grab.time != 0 &&
+               time > grab.time) {
+               send_input ("g", [GDK_GRAB_INVALID_TIME]);
+               break;
+           }
+       }
+
+       doGrab(id, owner_events, time, false);
+
+       send_input ("g", [GDK_GRAB_SUCCESS]);
+
+       break;
+
+      case 'u': // Ungrab
+       var time = base64_32(cmd, i);
+       i = i + 6;
+       send_input ("u", []);
+
+       if (grab.window != null) {
+           if (grab.time == 0 || time == 0 ||
+               grab.time < time)
+               grab.window = null;
+       }
+
+       break;
       default:
         alert("Unknown op " + command);
     }
@@ -349,41 +397,99 @@ function getPositionsFromEvent(ev, relativeId) {
     return res;
 }
 
+function getEffectiveEventTarget (id) {
+    if (grab.window != null) {
+       if (!grab.owner_events)
+           return grab.window;
+       if (id == 0)
+           return grab.window;
+    }
+    return id;
+}
+
 function on_mouse_move (ev) {
     var id = get_surface_id(ev);
+    id = getEffectiveEventTarget (id);
     var pos = getPositionsFromEvent(ev, id);
     send_input ("m", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]);
 }
 
 function on_mouse_over (ev) {
     var id = get_surface_id(ev);
+    real_window_with_mouse = id;
+    id = getEffectiveEventTarget (id);
     var pos = getPositionsFromEvent(ev, id);
     window_with_mouse = id;
     if (window_with_mouse != 0) {
-       send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]);
+       send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_NORMAL]);
     }
 }
 
 function on_mouse_out (ev) {
     var id = get_surface_id(ev);
+    var origId = id;
+    id = getEffectiveEventTarget (id);
     var pos = getPositionsFromEvent(ev, id);
 
     if (id != 0) {
-       send_input ("l", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]);
+       send_input ("l", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_NORMAL]);
     }
+    real_window_with_mouse = 0;
     window_with_mouse = 0;
 }
 
+function doGrab(id, owner_events, time, implicit) {
+    var pos;
+
+    if (window_with_mouse != id) {
+       if (window_with_mouse != 0) {
+           pos = getPositionsFromAbsCoord(last_x, last_y, window_with_mouse);
+           send_input ("l", [window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, time, GDK_CROSSING_GRAB]);
+       }
+       pos = getPositionsFromAbsCoord(last_x, last_y, id);
+       send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, time, GDK_CROSSING_GRAB]);
+       window_with_mouse = id;
+    }
+
+    grab.window = id;
+    grab.owner_events = owner_events;
+    grab.time = time;
+    grab.implicit = implicit;
+}
+
+function doUngrab() {
+    var pos;
+    if (real_window_with_mouse != window_with_mouse) {
+       if (window_with_mouse != 0) {
+           pos = getPositionsFromAbsCoord(last_x, last_y, window_with_mouse);
+           send_input ("l", [window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_UNGRAB]);
+       }
+       if (real_window_with_mouse != 0) {
+           pos = getPositionsFromAbsCoord(last_x, last_y, id);
+           send_input ("e", [real_window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_UNGRAB]);
+       }
+       window_with_mouse = real_window_with_mouse;
+    }
+    grab.window = null;
+}
+
 function on_mouse_down (ev) {
     var id = get_surface_id(ev);
+    id = getEffectiveEventTarget (id);
     var pos = getPositionsFromEvent(ev, id);
+    if (grab.window != null)
+       doGrab (id, false, ev.timeStamp, true);
     send_input ("b", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, ev.button]);
 }
 
 function on_mouse_up (ev) {
     var id = get_surface_id(ev);
+    id = getEffectiveEventTarget (id);
     var pos = getPositionsFromEvent(ev, id);
     send_input ("B", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, ev.button]);
+
+    if (grab.window != null && grab.implicit)
+       doUngrab();
 }
 
 var last_key_down = 0;
index e0a4dad44b2ea3c653984489e1f5c6cfe3ac14c9..6b2b672f3eba2d23a0933303e2d7d1a29cc6d412 100644 (file)
@@ -182,7 +182,7 @@ gdk_broadway_device_query_state (GdkDevice        *device,
 
       serial = broadway_output_query_pointer (broadway_display->output, impl->id);
 
-      reply = _gdk_broadway_display_block_for_input (display, 'q', serial);
+      reply = _gdk_broadway_display_block_for_input (display, 'q', serial, TRUE);
 
       if (reply != NULL)
        {
@@ -266,13 +266,93 @@ gdk_broadway_device_grab (GdkDevice    *device,
                          GdkCursor    *cursor,
                          guint32       time_)
 {
-  return GDK_GRAB_NOT_VIEWABLE;
+  GdkDisplay *display;
+  GdkBroadwayDisplay *broadway_display;
+  GdkWindowImplBroadway *impl;
+  guint32 serial;
+  char *reply;
+
+  display = gdk_device_get_display (device);
+  broadway_display = GDK_BROADWAY_DISPLAY (display);
+
+  if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+    {
+      /* Device is a keyboard */
+      return GDK_GRAB_SUCCESS;
+    }
+  else
+    {
+      /* Device is a pointer */
+
+      if (broadway_display->output)
+       {
+         impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
+
+         serial = broadway_output_grab_pointer (broadway_display->output,
+                                                impl->id, owner_events, time_);
+         reply = _gdk_broadway_display_block_for_input (display, 'g', serial, FALSE);
+         if (reply != NULL)
+           {
+             char *p;
+             char cmd;
+             guint32 reply_serial;
+             int res;
+
+             p = reply;
+
+             cmd = *p++;
+             reply_serial = (guint32)strtol(p, &p, 10);
+             p++; /* Skip , */
+
+             res = strtol(p, &p, 10);
+
+             return res;
+           }
+       }
+
+      return GDK_GRAB_NOT_VIEWABLE;
+    }
 }
 
+#define TIME_IS_LATER(time1, time2)                        \
+  ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) ||  \
+    (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 ))     \
+  )
+
 static void
 gdk_broadway_device_ungrab (GdkDevice *device,
                            guint32    time_)
 {
+  GdkDisplay *display;
+  GdkBroadwayDisplay *broadway_display;
+  GdkDeviceGrabInfo *grab;
+  guint32 serial;
+
+  display = gdk_device_get_display (device);
+  broadway_display = GDK_BROADWAY_DISPLAY (display);
+
+  if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+    {
+      /* Device is a keyboard */
+    }
+  else
+    {
+      /* Device is a pointer */
+
+      if (broadway_display->output)
+       {
+         serial = broadway_output_ungrab_pointer (broadway_display->output, time_);
+
+         gdk_display_flush (display);
+
+         grab = _gdk_display_get_last_device_grab (display, device);
+         if (grab &&
+             (time_ == GDK_CURRENT_TIME ||
+              grab->time == GDK_CURRENT_TIME ||
+              !TIME_IS_LATER (grab->time, time_)))
+           grab->serial_end = serial;
+       }
+    }
 }
 
 static GdkWindow *
index 9f90cc980e72c506c48864fd9562671c5c598950..f0e8dc1833fb0e54d080e659b41dd21f67081c78 100644 (file)
@@ -261,7 +261,8 @@ process_input_idle_cb (GdkBroadwayDisplay *display)
 
 /* Note: This may be called while handling a message (i.e. sorta recursively) */
 char *
-_gdk_broadway_display_block_for_input (GdkDisplay *display, char op, guint32 serial)
+_gdk_broadway_display_block_for_input (GdkDisplay *display, char op,
+                                      guint32 serial, gboolean remove_message)
 {
   GdkBroadwayDisplay *broadway_display;
   char *message;
@@ -295,8 +296,9 @@ _gdk_broadway_display_block_for_input (GdkDisplay *display, char op, guint32 ser
            msg_serial = (guint32)strtol(message+1, NULL, 10);
            if (msg_serial == serial)
              {
-               broadway_display->input_messages =
-                 g_list_delete_link (broadway_display->input_messages, l);
+               if (remove_message)
+                 broadway_display->input_messages =
+                   g_list_delete_link (broadway_display->input_messages, l);
                return message;
              }
          }
index 08ad05b7b52fd4bd6cd52ab0d41922592b37d782..26e23c7441f8f24175cbd119276ea5b9a0a43fb2 100644 (file)
@@ -124,7 +124,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
   GdkScreen *screen;
   GdkWindow *root, *window;
   char *p;
-  int button, dir,key;
+  int button, dir, key, detail;
   guint32 serial;
   guint64 time;
   GdkEvent *event = NULL;
@@ -142,6 +142,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
   switch (cmd) {
   case 'e': /* Enter */
     p = parse_pointer_data (p, &data);
+    p++; /* Skip , */
+    detail = strtol(p, &p, 10);
 
     display_broadway->last_x = data.root_x;
     display_broadway->last_y = data.root_y;
@@ -160,7 +162,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
        event->crossing.y = data.win_y;
        event->crossing.x_root = data.root_x;
        event->crossing.y_root = data.root_y;
-       event->crossing.mode = GDK_CROSSING_NORMAL;
+       event->crossing.mode = detail;
        event->crossing.detail = GDK_NOTIFY_ANCESTOR;
        gdk_event_set_device (event, display->core_pointer);
 
@@ -178,6 +180,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
     break;
   case 'l': /* Leave */
     p = parse_pointer_data (p, &data);
+    p++; /* Skip , */
+    detail = strtol(p, &p, 10);
 
     display_broadway->last_x = data.root_x;
     display_broadway->last_y = data.root_y;
@@ -196,7 +200,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
        event->crossing.x_root = data.root_x;
        event->crossing.y_root = data.root_y;
        event->crossing.mode = GDK_CROSSING_NORMAL;
-       event->crossing.detail = GDK_NOTIFY_ANCESTOR;
+       event->crossing.detail = detail;
        gdk_event_set_device (event, display->core_pointer);
 
        node = _gdk_event_queue_append (display, event);
@@ -310,6 +314,10 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
       }
 
     break;
+  case 'g':
+  case 'u':
+    _gdk_display_device_grab_update (display, display->core_pointer, NULL, serial);
+    break;
   case 'q':
     g_printerr ("Got unexpected query pointer reply w serial %d\n", serial);
     break;
index 5e60e895a09a2c5b83b3d17160fec2d580bd1ce9..a442439a69420e50ea3d72dee784ebabfb90fd6e 100644 (file)
@@ -189,7 +189,8 @@ gchar *_gdk_broadway_display_utf8_to_string_target (GdkDisplay  *display,
 GdkKeymap* _gdk_broadway_display_get_keymap (GdkDisplay *display);
 char * _gdk_broadway_display_block_for_input (GdkDisplay *display,
                                              char op,
-                                             guint32 serial);
+                                             guint32 serial,
+                                             gboolean remove);
 
 /* Window methods - testing */
 void     _gdk_broadway_window_sync_rendering    (GdkWindow       *window);